package org.vison.wonfu.framework.soa.spi;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.vison.wonfu.framework.commons.Constants;
import org.vison.wonfu.framework.soa.context.SoaApplicationContext;
import org.vison.wonfu.framework.soa.protocol.jsonRpc.JsonRpcRequest;
import org.vison.wonfu.framework.soa.protocol.jsonRpc.JsonRpcResponse;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 直接在过滤器中处理SOA服务请求
 */
@Component
public class SoaServiceFilter implements Filter {

    private final Logger logger = org.slf4j.LoggerFactory.getLogger(SoaServiceFilter.class);

    private final SoaServiceScanner serviceScanner;
    private final ObjectMapper objectMapper;
    private final Map<String, String> pathToServiceNameMap = new HashMap<>();

    public SoaServiceFilter(SoaServiceScanner serviceScanner, Environment environment) {
        this.serviceScanner = serviceScanner;
        String appid = environment.getProperty(Constants.APP_ID);
        this.objectMapper = (ObjectMapper) SoaApplicationContext.getCustomizeObjectMapper(appid);

        // 初始化路径到服务名的映射
        for (Map.Entry<String, SoaServiceScanner.ServiceDefinition> entry :
                serviceScanner.getServiceDefinitionMap().entrySet()) {
            pathToServiceNameMap.put(entry.getValue().getServicePath(), entry.getKey());
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 获取请求路径
        String requestURI = httpRequest.getRequestURI();
        String contextPath = httpRequest.getContextPath();
        String path = requestURI.substring(contextPath.length());

        // 检查是否是注册的SOA服务路径
        if (!pathToServiceNameMap.containsKey(path)) {
            // 不是SOA服务路径，继续过滤器链
            chain.doFilter(request, response);
            return;
        }

        // 只处理POST请求
        if (!"POST".equalsIgnoreCase(httpRequest.getMethod())) {
            httpResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
        }

        // 只处理JSON请求
        String contentType = httpRequest.getContentType();
        if (contentType == null || !contentType.startsWith("application/json")) {
            httpResponse.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
            return;
        }

        try {
            // 解析JSON-RPC请求
            JsonRpcRequest rpcRequest = objectMapper.readValue(
                    httpRequest.getInputStream(), JsonRpcRequest.class);

            // 准备JSON-RPC响应
            JsonRpcResponse rpcResponse = new JsonRpcResponse();
            rpcResponse.setId(rpcRequest.getId());

            // 查找服务定义
            String serviceName = pathToServiceNameMap.get(path);
            SoaServiceScanner.ServiceDefinition serviceDef =
                    serviceScanner.getServiceDefinitionMap().get(serviceName);

            // 查找方法
            String methodName = rpcRequest.getMethod();
            if (!serviceDef.getMethodMap().containsKey(methodName)) {
                httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
                rpcResponse.setError(new JsonRpcResponse.Error(-32601, "方法未找到", methodName));
                writeResponse(httpResponse, rpcResponse);
                return;
            }

            Object serviceBean = serviceScanner.getApplicationContext().getBean(serviceDef.getServiceClass());

            // 调用服务方法
            Object result = invokeServiceMethod(
                    serviceBean,
                    serviceDef.getMethodMap().get(methodName),
                    rpcRequest.getParams()
            );

            rpcResponse.setResult(result);

            // 返回响应
            writeResponse(httpResponse, rpcResponse);

        } catch (Exception e) {
            // 处理异常
            logger.error("处理请求时发生错误", e);
            JsonRpcResponse rpcResponse = new JsonRpcResponse();
//            rpcResponse.setError(new JsonRpcResponse.Error(-32603, "处理请求时发生错误", e.getMessage()));
            writeResponse(httpResponse, rpcResponse);
        }
    }

    private Object invokeServiceMethod(Object serviceBean, java.lang.reflect.Method method, Object params) throws IllegalAccessException, InvocationTargetException {
        Class<?>[] parameterTypes = method.getParameterTypes();

        if (parameterTypes.length == 0) {
            return method.invoke(serviceBean);
        }

        if (params instanceof Map) {
            Map<String, Object> paramsMap = (Map<String, Object>) params;
            Object[] args = new Object[parameterTypes.length];
            java.lang.reflect.Parameter[] parameters = method.getParameters();

            for (int i = 0; i < parameters.length; i++) {
                String paramName = parameters[i].getName();
                Object paramValue = paramsMap.get(paramName);
                args[i] = objectMapper.convertValue(paramValue, parameterTypes[i]);
            }

            return method.invoke(serviceBean, args);
        } else if (params instanceof java.util.List && ((java.util.List<?>) params).size() == parameterTypes.length) {
            java.util.List<?> paramsList = (java.util.List<?>) params;
            Object[] args = new Object[parameterTypes.length];

            for (int i = 0; i < parameterTypes.length; i++) {
                args[i] = objectMapper.convertValue(paramsList.get(i), parameterTypes[i]);
            }

            return method.invoke(serviceBean, args);
        } else {
            throw new IllegalArgumentException("参数格式不匹配");
        }
    }

    private void writeResponse(HttpServletResponse httpResponse, JsonRpcResponse rpcResponse) throws IOException {
        httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
        try (PrintWriter writer = httpResponse.getWriter()) {
            objectMapper.writeValue(writer, rpcResponse);
        }
    }
}